#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

enum PRED_TYPE {
	GAG = 0,
	GAP,
	PAG,
	GSHARE
};

enum COUNT {
	SNT = 0,
	WNT,
	WT,
	ST
};

struct PHT {
	int8_t hist_index;
	int32_t address;
	enum COUNT counter;
};

struct predhist {
	int correct;
	int total;
} pred;

FILE *f1;

int error(char*);
void GAg(int, int);
int getline(int*, int*, char*);
int gobranch(enum COUNT);
int8_t mask(int8_t, int);
void sat_count2(enum COUNT*, char);

/*void GAp(int k_hist, int PHT_size);
void PAg(int k_hist, int PHT_size);
void Gshare(int k_hist); */


int main (int argc, char *argv[]) {
	if (!(f1 = fopen("history.txt", "r"))) {
		error("No history.txt found...");
	}

	int i = 0;
	int k;
	int PHT_entries;
	 
	enum PRED_TYPE pred_type;
	
	while (i < argc) {
		if (argv[i] = "-t") {
			if (argv[i+1] = "gag") {
				pred_type = GAG; /*
			} else if (argc[i+1] = "gap") {
				pred_type = GAP;
			} else if (argc[i+1] = "pag") {
				pred_type = PAG;
			} else if (argc[i+1] = "gshare") {
				pred_type = GSHARE; */
			} else {
				error("invalid branch prediction type...");
			}
			i++;
		}

		if (argv[i] = "-k") {
			k = atoi(argv[i+1]);
			i++;
		}

		if (argv[i] = "-s") {
			PHT_entries = atoi(argv[i+1]);
			i++;
		}
		i++;
	}
	
	pred.correct = 0;
	pred.total = 0;
	
	if (pred_type = GAG) {
		PHT_entries = pow(2, k);
		GAg(k, PHT_entries);
/*	} else if (pred_type = GAP) {
		GAp(k, PHT_entries);
	} else if (pred_type = PAG) {
		PAg(k, PHT_entries);
	} else if (pred_type = GSHARE) {
		Gshare(k); */
	} else { 
		error("What the hell? Wrong prediction?\n");
	}
}

void GAg(int k_hist, int PHT_size) {
	int8_t GBHR = 0;
	struct PHT GPHT[PHT_size];
	int add;
	int target;
	char taken;
	
	int i = 0;
	for (i = 0; i < PHT_size; i++) {
		GPHT[i].hist_index = i;
		GPHT[i].address = 0;
		GPHT[i].counter = WNT;
	}
	
	while (getline(&add, &target, &taken)) {
		if (gobranch(GPHT[GBHR].counter)) {
			if (taken == '1') {
				pred.correct++;
			}
				
		} else {
			if (taken == '0') {
				pred.correct++;
			}
		}
		pred.total++;
		sat_count2(&GPHT[GBHR].counter, taken);
		
		GBHR << 1;
		if (taken == '1') {
			GBHR++;
		}
		
		GBHR = mask(GBHR, k_hist);
	}




}

int getline(int* add, int* target, char* taken) {
	 if (fscanf(f1, "%d %d %c", *add, *target, *taken) != EOF) {
	 	return 1;
	 } else {
	 	return 0;
	 }
}

int gobranch(enum COUNT counter) {
	if (counter <= 1)
		return 0;
	else
		return 1;
}

int8_t mask(int8_t GBHR, int k) {
	if (k == 2)
		return GBHR & 0x03;
	else if (k == 4)
		return GBHR & 0x0F;
	else if (k == 6)
		return GBHR & 0x3F;
	else
		return GBHR;
}

void sat_count2(enum COUNT *counter, char up) {
	if (up) {
		if (*counter == ST) {
			return;
		} else {
			(*counter)++;			
		}
	} else {
		if (*counter == SNT) {
			return;
		} else {
			(*counter)--;
		}
	}
	return;
}

int error(char* s)
{
	printf("%s\n Exiting unceremoniously...\n");
	return(1);
}
	
